<template>
  <div class='index-carousel'>
    <div class="img-wrap">
      <div class="img-box" @touchstart="fingerClick" @touchmove="fingerMove" @touchend="fingerEnd"
        @touchcancel="fingerLeave" @dragstart="stopToDrag" @contextmenu="stopToDrag" @selectstart="stopToDrag"
        @mousedown="mouseDown" @mousemove="binding && mouseMove($event)" @mouseup="binding && mouseUp($event)"
        @mouseleave.stop="binding && mouseLeave($event)">
        <template v-for="(v, i) in imgArr">
          <div class="box" :style="{
            left: getOffset(i).left,
            transitionDuration: getOffset(i)['transition-duration'],
            zIndex: getOffset(i)['z-index']
          }" @click="!clicked && turn(v.url)" :class="isMoving && 'moving'">
            <img :src="v.img" alt="图片加载失败">
          </div>
        </template>
      </div>
      <div class="tip-box">
        <div class="tip" v-for="(v, i) in imgArr" :key="i" :class="{ 'active': targetIndex % len === i }">
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    // 渲染数组
    imgArr: {
      type: Array,
      default: () => []
    },
    // 滑动时间
    carousel_time: {
      type: Number,
      default: 2
    },
  },
  data () {
    return {
      len: 0,           // 1 数组长度
      boxWidth: 0,      // 2 图片盒子长度
      targetIndex: 0,   // 3 当前索引
      offset: 0,        // 4 滑动长度
      isMoving: false,  // 5 是否正在滑动中
      timer: null,      // 6 定时器索引

      binding: false,   // 1 电脑端是否绑定事件
      clicked: false,   // 2 电脑端是否触发点击事件

      oX: 0,            // 1 初始点X
      oY: 0,            // 2 初始点Y
      nX: 0,            // 3 变化点X
      nY: 0,            // 4 变化点Y
      once: true,       // 5 防止 判断滑动方向事件 执行多次
      turnLeft: false,  // 6 往左滑动
    };
  },
  methods: {
    // 1 组件初始化
    init () {
      this.initArr()
      this.startTimer()
      this.listenPage()
    },

    // 2 初始化数组
    initArr () {
      this.len = this.imgArr.length
      this.targetIndex = this.imgArr.length
    },

    // 3 返回滑动值
    getOffset (index) {
      // 通过滑动方向来判断当前图和下张图有过渡效果
      if (index === this.targetIndex % this.len) {
        return {
          'left': `${this.offset}%`,
          'transition-duration': '400ms',
          'z-index': 2
        }
      }
      // 往右滑是当前这张和下一张
      else if (index === this.nextIndex % this.len) {
        return {
          'left': `${this.offset + 100}%`,
          'transition-duration': this.turnLeft ? '400ms' : '0ms',
        }
      }
      // 往左滑是当前这张和上一张
      else if (index === this.lastIndex % this.len) {
        return {
          'left': `${this.offset - 100}%`,
          'transition-duration': !this.turnLeft ? '400ms' : '0ms',
        }
      }
      // 其它在其他位置存放即可
      else {
        return {
          'left': `1000%`,
          'transition-duration': '0ms'
        }
      }
    },

    // 4 移动共用事件
    judgeDirection () {
      // 1 该事件只执行一次
      this.once = false
      // 2 获取比值
      let changeX = Math.abs(this.oX - this.nX)
      let changeY = Math.abs(this.oY - this.nY)
      // tan90' = 对比/临边
      let bizhi = changeY / changeX
      // 3 判断方向
      if (bizhi > 1) {
        // 上下滑动不影响轮播
        this.isMoving = false
      } else {
        // 左右滑动停止轮播
        this.isMoving = true
        this.clearTimer()
      }
      // 4 电脑端不触发点击
      this.clicked = true
    },
    judgeMove () {
      // 0 取消滑动
      this.isMoving = false
      // 1 可以跳转，判断左右
      if (Math.abs(this.distance) > 12) {
        this.distance > 0 ? (this.targetIndex--) : (this.targetIndex++)
      }
      // 2 拒绝跳转，变为初始位置
      else {
        this.offset = 0
      }
    },

    // 5 手机端移动事件
    fingerClick (e) {
      // 获取初始位置
      this.oX = e.touches[0].clientX
      this.oY = e.touches[0].clientY
    },
    fingerMove (e) {
      // 1 获取变化位置
      this.nX = e.touches[0].clientX
      this.nY = e.touches[0].clientY
      // 2 获取滑动方向，判断是否可以滑动
      this.once && this.judgeDirection()
      // 3 左右滑动时不带动屏幕
      this.isMoving && e.preventDefault()
      // 4 左右滑动时触发移动事件
      this.isMoving && (this.offset = this.changeLen)
    },
    fingerEnd (e) {
      // 1 获取最终位置
      this.nX = e.changedTouches[0].clientX
      this.nY = e.changedTouches[0].clientY
      // 2 判断往左滑动
      this.changeLen > 0 && this.isMoving && (this.turnLeft = true)
      // 3 左右滑动时判断是否跳转
      this.isMoving && this.judgeMove()
      // 4 初始化判断方向变量
      this.once = true
      // 5 初始化偏移量
      this.offset = 0
      // 6 等页面变化后，初始化变量
      setTimeout(() => {
        this.turnLeft = false
        this.clicked = false
      }, 100);
      // 7 开启轮播
      this.startTimer()
    },
    fingerLeave (e) {
      this.fingerEnd(e)
    },

    // 6 电脑端移动事件
    stopToDrag (e) {
      e.returnValue = false
      return false
    },
    mouseDown (e) {
      // 1 绑定事件
      this.binding = true
      // 2 获取初始位置
      this.oX = e.clientX;
    },
    mouseMove (e) {
      // 1 获取变化位置
      this.nX = e.clientX;
      // 2 获取滑动方向，判断是否可以滑动
      this.once && this.judgeDirection()
      // 3 触发移动事件
      this.offset = this.changeLen
    },
    mouseUp (e) {
      // 1 获取最终位置
      this.nX = e.clientX;
      // 2 判断往左滑动
      this.changeLen > 0 && this.isMoving && (this.turnLeft = true)
      // 3 左右滑动时判断是否跳转
      this.isMoving && this.judgeMove()
      // 4 初始化判断方向变量
      this.once = true
      // 5 初始化偏移量
      this.offset = 0
      // 6 初始化绑定判断
      this.binding = false
      // 7 等页面变化后，初始化变量
      setTimeout(() => {
        this.turnLeft = false
        this.clicked = false
      }, 100);
      // 8 开启轮播
      this.startTimer()

    },
    mouseLeave (e) {
      this.mouseUp(e)
    },

    // 7 图片定时器事件
    startTimer () {
      if (this.timer === null) {
        // 设置定时器，改变索引
        this.timer = setInterval(() => {
          this.targetIndex++
        }, this.carousel_time * 1000)
      }
    },
    clearTimer () {
      // 1 标识存在清除定时器
      this.timer && clearInterval(this.timer)
      // 2 清空标识
      this.timer = null
    },

    // 8 点击事件
    turn (url) {
      console.log('触发跳转: ' + url);
    },

    // 9 监听页面显示和隐藏，
    listenPage () {
      // 可能需要兼容苹果端：Safari不会按预期触发visibilitychange
      document.addEventListener('visibilitychange', () => {
        // hidden & visible
        if (document.visibilityState === 'hidden') {
          // 页面隐藏时清除定时器
          this.clearTimer()
        } else if (document.visibilityState === 'visible') {
          // 页面恢复启动定时器
          this.startTimer()
        }
      })
    },
  },
  computed: {
    // 1 图片当前索引的前一个和后一个
    nextIndex () {
      return this.targetIndex + 1
    },
    lastIndex () {
      return this.targetIndex - 1
    },
    // 2 获取滑动距离
    distance () {
      return this.nX - this.oX
    },
    // 3 滑动距离所占百分比（负数为往右，正数为往左）
    changeLen () {
      return (this.distance / this.boxWidth) * 100
    }
  },
  watch: {
    targetIndex (newV) {
      if (newV === 0) {
        this.targetIndex = this.len
      }
      else if (newV === this.len + 1) {
        this.targetIndex = 1
      }
    }
  },
  created () {
    this.init()
  },
  mounted () {
    this.boxWidth = this.$el.offsetWidth
  },
};
</script>

<style lang="scss" scoped>
.index-carousel {
  width: 100%;
  position: relative;

  .img-wrap {
    @include wh(100%, 195);
    overflow: hidden;
    cursor: pointer;
    position: relative;

    .img-box {
      @include wh(100%, 100%);
      @include vC;
      position: relative;

      .box {
        @include wh(100%, 100%);
        position: absolute;
        transition: left ease-in-out;

        img {
          @include img;
        }
      }
    }

    .tip-box {
      position: absolute;
      right: rem(10);
      bottom: rem(49);
      @include vC;
      z-index: $level3;

      .tip {
        @include wh(8, 3);
        background-color: #fff;
        margin-left: rem(6);
      }

      .active {
        background-color: #0162ff;
      }
    }

    .moving {
      transition: unset !important;
    }
  }
}
</style>
